package com.ry.aspect;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.UUID;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import com.ry.redis.utils.JedisUtil;

import redis.clients.jedis.Jedis;

/**
 * redis 分布式锁，使用aop方式
 * @author Administrator
 *
 */
@Aspect
@Component
public class RedisLockAspect {

	// 配置织入点
	@Pointcut("@annotation(com.ry.aspect.annotation.RedisLock)")
	public void doOperation() {
		
	}

	
	
	@Around("doOperation()")
	public Object around(ProceedingJoinPoint point) throws Throwable {

		MethodSignature signature = (MethodSignature) point.getSignature();
		Method method = signature.getMethod();

		////这里是锁的方法名，也可以锁资源
        //比如转工单的时候，通过工单的id来锁定，推荐锁资源
		String key = "lock_" + method.getName();
		
		String requestId = UUID.randomUUID().toString();
		
		Jedis jedis = null;
		try {
			jedis = JedisUtil.getJedis();
			boolean isLock = tryGetDistributedLock(jedis, key, requestId, 10000);
            if (isLock) {
                System.out.println(Thread.currentThread().getName() + "获取到锁了");

    			return point.proceed();
        			
            }else {
            	return "do not get lock";
            }
			
		} finally {

            boolean isLease = releaseDistributedLock(jedis, key, requestId);
            System.out.println(Thread.currentThread().getName() + "释放锁 " + (isLease==true?"成功":"失败"));
            
			if(jedis != null) {
				jedis.close();
			}
		}
		
	}
	
	private static final String LOCK_SUCCESS = "OK";
    //SET IF NOT EXIST的意思
    private static final String SET_IF_NOT_EXIST = "NX";
    //PX：单位毫秒，EX：单位秒
    private static final String SET_WITH_EXPIRE_TIME = "PX";
	
	/**
     * 尝试获取分布式锁
     *
     * @param jedis      Redis客户端
     * @param lockKey    锁
     * @param requestId  请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        boolean iden = false;

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            iden = true;
        }else{
            try {
                Thread.sleep(500);
                int count = 0;
                while(count < 5){
                    result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

                    if(LOCK_SUCCESS.equals(result)){
                        iden = true;
                        break;
                    }else{
                        Thread.sleep(500);
                    }
                    count++;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return iden;
    }
	
    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 释放分布式锁
     * @param jedis         Redis客户端
     * @param lockKey       锁
     * @param requestId     request请求id
     * @return  是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
    
    
}
